/* *
 * --ライセンスについて--
 *
 * 「本ファイルの内容は Mozilla Public License Version 1.1 (「本ライセンス」)
 * の適用を受けます。
 * 本ライセンスに従わない限り本ファイルを使用することはできません。
 * 本ライセンスのコピーは http://www.mozilla.org/MPL/ から入手できます。
 *
 * 本ライセンスに基づき配布されるソフトウェアは、「現状のまま」で配布されるものであり、
 * 明示的か黙示的かを問わず、いかなる種類の保証も行われません。
 * 本ライセンス上の権利および制限を定める具体的な文言は、本ライセンスを参照してください。
 *
 * オリジナルコードおよび初期開発者は、N_H (h.10x64@gmail.com) です。
 *
 * N_H によって作成された部分の著作権表示は次のとおりです。
 *
 * Copyright (C)N_H 2012
 *
 * このファイルの内容は、上記に代えて、
 * GNU General License version2 以降 (以下 GPL とする)、
 * GNU Lesser General Public License Version 2.1 以降 (以下 LGPL とする)、
 * の条件に従って使用することも可能です。
 * この場合、このファイルの使用には上記の条項ではなく GPL または LGPL の条項が適用されます。
 * このファイルの他者による使用を GPL または LGPL の条件によってのみ許可し、
 * MPL による使用を許可したくない対象者は、上記の条項を削除することでその意思を示し、
 * 上記条項を GPL または LGPL で義務付けられている告知およびその他の条項に置き換えてください。
 * 対象者が上記の条項を削除しない場合、
 * 受領者は MPL または GPL または LGPL ライセンスのいずれによってもこのファイルを
 * 使用することができます。」
 *
 * -- License --
 *
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License。You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND、either express or implied。See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Initial Developer of the Original Code is
 *   N_H (h.10x64@gmail.com).
 *
 * Portions created by the Initial Developer are Copyright (C)N_H 2012
 * the Initial Developer。All Rights Reserved.
 *
 * Alternatively、the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL")、or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above。If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL、and not to allow others to
 * use your version of this file under the terms of the MPL、indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL。If you do not delete
 * the provisions above、a recipient may use your version of this file under
 * the terms of any one of the MPL、the GPL or the LGPL.
 *
 * */
package com.magiciansforest.audio.soundrenderer.logic;

import com.jme3.animation.Skeleton;
import com.jme3.asset.plugins.FileLocator;
import com.magiciansforest.audio.soundrenderer.ApplicationBase;
import com.magiciansforest.audio.soundrenderer.event.CharacterChangeEventListener;
import com.magiciansforest.audio.soundrenderer.event.SoundSourceChangeEvent;
import com.magiciansforest.audio.soundrenderer.event.SoundSourceChangeEventListener;
import com.magiciansforest.audio.soundrenderer.logic.mmd.ItemHolder;
import com.magiciansforest.audio.soundrenderer.logic.mmd.VMD;
import com.magiciansforest.audio.soundrenderer.logic.sound.SoundSource;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import projectkyoto.jme3.mmd.PMDNode;
import projectkyoto.jme3.mmd.UpdateControl;
import projectkyoto.jme3.mmd.vmd.VMDControl;
import projectkyoto.mmd.file.VMDFile;
import projectkyoto.mmd.file.VMDMotion;
import projectkyoto.mmd.file.VMDSkin;

/**
 *
 * @author N_H
 */
public class MMDCharacter implements SoundSourceChangeEventListener {

    private long id;
    private File pmdFile;
    protected ItemHolder model = null;
    protected List<SoundSource> sources = new ArrayList<SoundSource>();
    private String vmdFileName = "### NULL ###";
    private String vmdFilePath = "null";
    protected VMDFile motion = null;
    private VMDControl vmdControl;
    private TimeKeeper timeKeeper;
    private ApplicationBase app;
    private List<CharacterChangeEventListener> listeners = new ArrayList<CharacterChangeEventListener>();

    public MMDCharacter(ApplicationBase app, TimeKeeper timeKeeper, long id, File pmdFile) {
        app.getAssetManager().registerLocator(pmdFile.getParent(), FileLocator.class);
        this.app = app;
        
        PMDNode pmdNode = (PMDNode) app.getAssetManager().loadModel(pmdFile.getName());
        if (pmdNode != null) {
            this.model = new ItemHolder(app, pmdNode);
            pmdNode.addControl(new UpdateControl(pmdNode));
        } else {
            throw new IllegalArgumentException("PMD File loading failed");
        }

        this.id = id;
        this.pmdFile = pmdFile;
        this.timeKeeper = timeKeeper;
    }

    public void update(float tpf) {
        if (vmdControl != null) {
            vmdControl.update(tpf);
        }
    }

    @Override
    public void SoundSourceChange(SoundSourceChangeEvent e) {
        if (e.getStatus() == SoundSourceChangeEvent.REMOVED) {
            for (int i = 0; i < sources.size(); i++) {
                SoundSource src = sources.get(i);
                if (src.getID() == e.getID()) {
                    model.removeItem(src);
                    sources.remove(src);
                }
            }
        }
    }

    public long getID() {
        return this.id;
    }

    public String getName() {
        return model.getModel().getName();
    }

    public String getPMDFilePath() {
        return pmdFile.getAbsolutePath();
    }

    public String getVMDFilePath() {
        return vmdFilePath;
    }

    public int getMotionCount() {
        if (motion == null) {
            return 0;
        }
        return motion.getMotionCount();
    }

    public int getSkinCount() {
        if (motion == null) {
            return 0;
        }
        return motion.getSkinCount();
    }

    public String getVMDFileName() {
        return vmdFileName;
    }

    public String getVMDModelName() {
        if (motion == null) {
            return "### NULL ###";
        }

        return motion.getVmdModelName();
    }

    public boolean hasVMD() {
        return (motion != null);
    }

    public List<String> getBoneNames() {
        if (model == null) {
            return new ArrayList<String>();
        }

        ArrayList<String> ret = new ArrayList<String>();
        for (int i = 0; i < model.getModel().getSkeleton().getBoneCount(); i++) {
            ret.add(model.getModel().getSkeleton().getBone(i).getName());
        }
        return ret;
    }

    VMDMotion getVMDMotion(int index) {
        if (motion == null || index >= motion.getMotionCount() || index < 0) {
            return null;
        }
        return motion.getMotionArray()[index];
    }

    VMDSkin getVMDSkin(int index) {
        if (motion == null || index >= motion.getSkinCount() || index < 0) {
            return null;
        }
        return motion.getSkinArray()[index];
    }

    Skeleton getSkeleton() {
        if (model == null) {
            return null;
        }
        return model.getModel().getSkeleton();
    }

    ItemHolder getModel() {
        return this.model;
    }

    void setVMD(VMD vmd) {
        VMDFile vmdFile = vmd.toVMDFile();
        if (model != null) {
            if (vmd == null) {
                vmdFileName = "### NULL ###";
                vmdFilePath = "null";
            } else {
                vmdFileName = vmd.getFileName();
                vmdFilePath = vmd.getFilePath();
            }

            motion = vmdFile;

            if (vmdControl != null) {
                vmdControl.setPause(true);
                vmdControl.setEnabled(false);
            }
            if (vmd == null) {
                vmdControl = null;
            } else {
                vmdControl = new VMDControl(this.model.getModel(), vmdFile);
                
                timeKeeper.addControl(vmdControl);
                //app.getRootNode().addControl(vmdControl);
                
                vmdControl.setEnabled(true);
                vmdControl.setPause(true);
            }
        }
    }

    void addSoundSource(String boneName, MMDSoundSource source) {
        if (model != null) {
            model.addItem(boneName, source.getSource());
        }
        if (!sources.contains(source.getSource())) {
            sources.add(source.getSource());
        }
    }

    void removeSource(SoundSource source) {
        if (model != null) {
            model.removeItem(source);
        }
        sources.remove(source);
    }

    void removeAllSources() {
        for (SoundSource src : sources) {
            model.removeItem(src);
        }
        sources.clear();
    }

    List<SoundSource> getSoundSources() {
        return this.sources;
    }

    int getSourceCount() {
        return sources.size();
    }

    boolean containsBone(String boneName) {
        if (model == null) {
            return false;
        }
        return (model.getModel().getSkeleton().getBone(boneName) == null);
    }

    void setEnabled(boolean enabled) {
        if (vmdControl != null) {
            vmdControl.setEnabled(enabled);
        }
    }

    boolean isEnabled() {
        if (vmdControl == null) {
            return false;
        }
        return vmdControl.isEnabled();
    }

    void setPause(boolean pause) {
        if (vmdControl != null) {
            vmdControl.setPause(pause);
        }
    }

    void setFrameNo(int frameNo) {
        if (vmdControl != null) {
            vmdControl.setFrameNo(frameNo);
        }
        if (model != null) {
            model.update();
        }
    }

    int getFrameNo() {
        if (vmdControl != null) {
            return vmdControl.getFrameNo();
        } else {
            return -1;
        }
    }

    int getLastFrameNo() {
        if (vmdControl != null) {
            return vmdControl.getLastFrameNo();
        } else {
            return -1;
        }
    }

    boolean isPause() {
        if (vmdControl != null) {
            return vmdControl.isPause();
        } else {
            return false;
        }
    }

    @Override
    public String toString() {
        return Long.toString(this.getID()) + ": " + this.getName();
    }
}
